

package com.chinda.common.aspect;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.PropertyFilter;
import com.chinda.common.annotation.SysLog;
import com.chinda.common.enums.MessageEnum;
import com.chinda.common.utils.IPUtils;
import com.chinda.common.utils.SecurityUtil;
import com.chinda.common.utils.SpringContextUtils;
import com.chinda.modules.sys.entity.SysLogEntity;
import com.chinda.modules.sys.service.SysLogService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;

/**
 * 系统日志，切面处理类
 *
 * @author Wang Chinda
 * @date 2021/6/19
 * @see
 * @since 1.0
 */
@Slf4j
@Aspect
@Component
@AllArgsConstructor
public class SysLogAspect {
	private final SysLogService sysLogService;
	
	@Pointcut("@annotation(com.chinda.common.annotation.SysLog)")
	public void logPointCut() {
		// 切面
	}

	@Around("logPointCut()")
	public Object around(ProceedingJoinPoint point) throws Throwable {
		long beginTime = System.currentTimeMillis();
		//执行方法
		Object result = point.proceed();
		//执行时长(毫秒)
		long time = System.currentTimeMillis() - beginTime;

		//保存日志
		saveSysLog(point, time);

		return result;
	}

	private void saveSysLog(ProceedingJoinPoint joinPoint, long time) {
		MethodSignature signature = (MethodSignature) joinPoint.getSignature();
		Method method = signature.getMethod();

		SysLogEntity sysLog = new SysLogEntity();
		SysLog syslog = method.getAnnotation(SysLog.class);
		if(syslog != null){
			// 注解上的描述
			sysLog.setOperation(syslog.value());
		}

		//请求的方法名
		String className = joinPoint.getTarget().getClass().getName();
		String methodName = signature.getName();
		sysLog.setMethod(className + StrUtil.DOT + methodName + "()");

		//获取request
		HttpServletRequest request = SpringContextUtils.getHttpServletRequest();
		//设置IP地址
		sysLog.setIp(IPUtils.getIpAddr(request));

		//请求的参数
		sysLog.setParams(getRequestParams(request, joinPoint));


		//用户名
		String username = SecurityUtil.getUserName();
		sysLog.setUsername(username);

		sysLog.setTime(time);
		//保存系统日志
		sysLogService.save(sysLog);
	}

	/**
	 * 获取请求参数
	 * @param request
	 * @param joinPoint
	 * @return
	 */
	private String getRequestParams(HttpServletRequest request, JoinPoint joinPoint) {
		try {
			String httpMethod = request.getMethod();
			StringBuilder builder = new StringBuilder();
			if ("POST".equals(httpMethod) || "PUT".equals(httpMethod) || "PATCH".equals(httpMethod)) {
				Object[] paramsArray = joinPoint.getArgs();
				Object[] arguments  = new Object[paramsArray.length];
				for (int i = 0; i < paramsArray.length; i++) {
					if (paramsArray[i] instanceof BindingResult || paramsArray[i] instanceof ServletRequest || paramsArray[i] instanceof ServletResponse || paramsArray[i] instanceof MultipartFile) {
						// ServletRequest不能序列化，从入参里排除，否则报异常：java.lang.IllegalStateException: It is illegal to call this method if the current request is not in asynchronous mode (i.e. isAsyncStarted() returns false)
						// ServletResponse不能序列化 从入参里排除，否则报异常：java.lang.IllegalStateException: getOutputStream() has already been called for this response
						continue;
					}
					arguments[i] = paramsArray[i];
				}
				// 日志数据太长的直接过滤掉
				PropertyFilter propertyfilter = (o, name, value) -> !(value!=null && value.toString().length() > 500);
				builder.append(JSON.toJSONString(arguments, propertyfilter));
			} else {
				MethodSignature signature = (MethodSignature) joinPoint.getSignature();
				Method method = signature.getMethod();
				// 请求的方法参数值
				Object[] args = joinPoint.getArgs();
				// 请求的方法参数名称
				LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
				String[] paramNames = u.getParameterNames(method);
				if (args != null && paramNames != null) {
					for (int i = 0; i < args.length; i++) {
						builder.append("  " + paramNames[i] + ": " + args[i]);
					}
				}
			}
			return builder.toString();
		} catch (Exception e) {
			log.warn(MessageEnum.SYS_CHECK_0013.getMsg(), e);
		}
		return null;
	}
}
